package com.knowledge.review.designPatterns.creatormode.singleton;

import java.io.*;
import java.lang.reflect.Constructor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author XFW
 * @date 15:24 2024/4/25
 * @description 单例模式
 **/

/**
 * 单例模式~饿汉模式
 */
public class HungryChinese {

    private HungryChinese() {
    }

    private static final HungryChinese hungryChinese = new HungryChinese();

    public static HungryChinese getInstance() {
        return hungryChinese;
    }

}

class HungryChineseTest {
    public static void main(String[] args) {
        //获取单例类的对象，因为对象私有，只能通过方法去获取
        HungryChinese instance = HungryChinese.getInstance();
        HungryChinese instance1 = HungryChinese.getInstance();
        //判断是否为同一个对象
        System.out.println(instance.equals(instance1));
    }
}

/**
 * 单例模式~懒汉模式
 */
class LayTest {

    private LayTest() {
    }

    private static LayTest layTest;

    public static LayTest getInstance() {
        if (layTest == null) {
            layTest = new LayTest();
        }
        return layTest;
    }
}

class LayTest1 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        Runnable task = () -> {
            LayTest instance = LayTest.getInstance();
            // 通过观察hashCode码是否相同，不同可能在instance出现了null的情况
            System.out.println(Thread.currentThread().getName() + " - " + instance.hashCode());
        };

        executorService.submit(task);
        executorService.submit(task);

        executorService.shutdown();
    }
}

/**
 * 单例模式~双检锁
 */
class DoubleCheckLock {
    private DoubleCheckLock() {
    }

    // 如果不加volatile关键字的话，双检锁也不一定安全
    private static volatile DoubleCheckLock doubleCheckLock;

    public static DoubleCheckLock getInstance() {
        if (doubleCheckLock == null) {
            synchronized (DoubleCheckLock.class) {
                doubleCheckLock = new DoubleCheckLock();
            }
        }
        return doubleCheckLock;
    }
}

class DoubleCheckLockTest {
    public static void main(String[] args) {
        // 使用双检锁线程就一定安全了吗？答案当然不安全，为什么，原子不可见以及指令重排问题
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        Runnable task = () -> {
            DoubleCheckLock instance = DoubleCheckLock.getInstance();
            // 通过观察hashCode码是否相同，不同可能在instance出现了null的情况
            System.out.println(Thread.currentThread().getName() + " - " + instance.hashCode());
        };
        executorService.submit(task);
        executorService.submit(task);
        executorService.shutdown();
    }
}

/**
 * 单例模式~内部类
 */
class InnerClass {
    private InnerClass() {
        /*
         *  避免反射和序列化被暴力破解
         */
//        if (InnerClass1.innerClass != null){
//            throw new RuntimeException("不允许反射");
//        }

    }

    private static class InnerClass1 {
        private static InnerClass innerClass = new InnerClass();
    }

    public static InnerClass getInstance() {
        return InnerClass1.innerClass;
    }

    private Object readResolve() {
        return InnerClass1.innerClass;
    }
}

class InnerClassTest {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Runnable task = () -> {
            InnerClass instance = InnerClass.getInstance();
            System.out.println(Thread.currentThread().getName() + " - " + instance.hashCode());
        };
        executorService.execute(task);
        executorService.execute(task);
        executorService.shutdown();
    }
}

/**
 * 反射和序列化都能对我们的单例进行破坏
 */
class DestroySingleton {
    public static void main(String[] args) throws Exception {
        Class<InnerClass> singleton5Class = InnerClass.class;
        Constructor<InnerClass> singleton5Constructor = singleton5Class.getDeclaredConstructor();
        //暴利反射
        singleton5Constructor.setAccessible(true);
        InnerClass singleton5 = singleton5Constructor.newInstance();
        System.out.println(singleton5 == InnerClass.getInstance());
    }
}


class SerializableSingleton {
    public static void main(String[] args) throws Exception {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("temp.obj"));
        objectOutputStream.writeObject(InnerClass.getInstance());

        File file = new File("temp.obj");
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
        InnerClass singleton5 = (InnerClass) objectInputStream.readObject();

        System.out.println(singleton5 == InnerClass.getInstance());
    }
}

/**
 * 单例模式~枚举模式
 */
enum EnumSington {
    INSTANCE
}

class enumTest {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        Runnable task = () -> {
            EnumSington instance = EnumSington.INSTANCE;
            System.out.println(Thread.currentThread().getName() + " - " + instance.hashCode());
        };
        executorService.execute(task);
        executorService.execute(task);
        executorService.shutdown();
    }
}

/**
 * 小结：为什么使用单例模式，单例模式的主要作用就是创建单个对象，创建单例模式总共有五种方式
 * 第一种和第二种分别为饿汉和懒汉模式，两者区别就是加载问题，
 * 1、饿汉模式每次都会加载创建一个对象出来，因为是static（静态）修饰，所以线程是安全的，并且不支持
 * 延时加载，获取实例速度比较快（静态），缺点就是如果对象比较大的话，并且这个对象一直没有使用
 * 到就会造成资源浪费；
 * 2、懒汉则是先定义在进行创建，缺点就是线程不安全，懒汉要想安全就要使用锁，但会导致并发度特别低，
 * 因为在方法上加上了锁，再次演变就是双检锁模式，保证可见性，并且可以实现指令重排；
 * 3、以静态内部类实现单例模式，优点~线程安全，节约资源，获取对象速度较快，因为是静态内部类，避免了多线程同步问题
 * 缺点：类加载过程变慢，反序列化和反射都会对我们单例进行破坏，决绝此问题就要在内部类之前加上一个方法，避免被暴力破坏
 * 4、特点：阻止反射对单例的破坏，在反射方法中不允许使用反射来创建枚举对象 阻止序列化对单例的破坏，在序列化中，
 * 只是将枚举对象的name属性输出到了结果中，返序列化的时候，就会通过Enum的valueOf方法，来根据名字去查找对应的枚举
 * 对象，因此反序列化后，他们的对象是同一个。
 */